package mud;

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.SwingUtilities;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;

import terminal.Terminal;
import terminal.TerminalServer;
import terminal.TerminalServer.TerminalProtocol;

import mud.player.Player;

/** The main MUD class */
public class Mud {

    /** the port on which to listen for incoming connections */
    private static final int DEFAULT_PORT = 23;

    /** the logger */
    private static final Logger log = Logger.getLogger(Mud.class.getName());

    /** the virtual world */
    private final World world;

    /** indicates if the server should stop accepting new connections */
    private boolean shutdown = false;
    
    /**
     * Program entry point
     * @param args the command line arguments
     * @throws JAXBException if a JAXB exception occurs
     * @throws IOException if an I/O exception occurs
     */
    public static void main(String[] args) throws JAXBException, IOException {
        
        final Mud mud = new Mud();
        final TerminalServer server = new TerminalServer();
        server.openPort(DEFAULT_PORT, TerminalProtocol.TELNET);
        
        while (!mud.shutdown) {
            final Terminal terminal = server.accept();
            final Player player = new Player(terminal);
            Thread playerThread = new Thread(new Runnable() {
    
                @Override
                public void run() {
                    assert !SwingUtilities.isEventDispatchThread();
    
                    try {
                        player.handleLogin(mud.world);
                        player.handleLogin(mud.world);
                        for (;;) {
                            player.handleCommand();
                        }
                    } catch (EOFException e) {
                        log.finest("Received EOF from " + terminal);
                    } catch (IOException e) {
                        log.log(Level.SEVERE, "Network error from " + terminal, e);
                    } catch (InvocationTargetException e) {
                        log.finest("Exception on " + terminal);
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        log.finest("Interruption on " + terminal);
                        e.printStackTrace();
                    }
    
    //                SwingUtilities.invokeLater(new Runnable() {
    //                    @Override
    //                    public void run() {
    //                        Room room = unit.getRoom();
    //                        if (room != null) {
    //                            room.removeEntity(unit);
    //                        }
    //                    }
    //                });
    //                server.disconnect(terminal);
                }
    
            });
            playerThread.start();
    
            log.info("Connection: " + terminal);
        }
    }
    
    /**
     * Constructs a new MUD with a world as specified in world.xml
     * @throws JAXBException if a JAXB exception occurs
     */
    public Mud() throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(World.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        unmarshaller.setEventHandler(new ValidationEventHandler() {
            @Override
            public boolean handleEvent(ValidationEvent event) {
                log.severe(event.toString());
                return true;
            }
        });
        world = (World) unmarshaller.unmarshal(new File("world.xml"));
        log.info("Loaded " + world.toString());
    }
    
    /**
     * @return the virtual world
     */
    public World getWorld() {
        assert SwingUtilities.isEventDispatchThread();
        return world;
    }
}
